package com.bootdo.rabbitMQ.producer;

import com.alibaba.fastjson.JSON;
import com.bootdo.rabbitMQ.config.RabbitConstant;
import com.bootdo.rabbitMQ.direct.DirectConstant;
import com.bootdo.rabbitMQ.direct.DirectNews;
import com.bootdo.rabbitMQ.entity.MessageVo;
import com.bootdo.rabbitMQ.fanout.FanoutConstant;
import com.bootdo.rabbitMQ.topic.TopicConstant;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.amqp.core.Message;
import org.springframework.amqp.rabbit.connection.CorrelationData;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import javax.annotation.PostConstruct;
import java.util.List;
import java.util.UUID;

/**
 * @Author wukq
 * @Date: 2020/4/14
 * @Description:
 *
 *  ConfirmCallback
 * 通过实现ConfirmCallBack接口，消息发送到交换器Exchange后触发回调。
 * ReturnCallback
 * 通过实现ReturnCallback接口，如果消息从交换器发送到对应队列失败时触发
 */
@Component
public class Sender implements RabbitTemplate.ConfirmCallback, RabbitTemplate.ReturnCallback {

    private static final Logger log = LoggerFactory.getLogger(Sender.class);

    @Autowired
    private RabbitTemplate rabbitTemplate;

    /**
     * 被@PostConstruct修饰的方法会在服务器加载Servlet的时候运行，并且只会被服务器执行一次。
     *
     * @PostConstruct 注解的方法将会在依赖注入完成后被自动调用。
     */
    @PostConstruct
    public void init() {
        log.info("@PostConstruct init()方法：---------------");
        rabbitTemplate.setConfirmCallback(this);
        rabbitTemplate.setReturnCallback(this);
    }

    /**
     * 消息确认要开启配置publisher-confirms: true 或者 publisher-confirm-type: correlated
     * <p>
     * 1.spring.rabbitmq.publisher-confirm发布确认属性配置
     * 如果该属性为true
     * <p>
     * 2.spring.rabbitmq.publisher-confirm-type新版发布确认属性有三种确认类型
     * NONE值是禁用发布确认模式，是默认值
     * CORRELATED值是发布消息成功到交换器后会触发回调方法，如1示例
     * SIMPLE值经测试有两种效果，其一效果和CORRELATED值一样会触发回调方法，
     * 其二在发布消息成功后使用rabbitTemplate调用waitForConfirms或waitForConfirmsOrDie方法等待broker节点返回发送结果，
     * 根据返回结果来判定下一步的逻辑，要注意的点是waitForConfirmsOrDie方法如果返回false则会关闭channel，则接下来无法发送消息到broker;
     */
    @Override
    public void confirm(CorrelationData correlationData, boolean ack, String cause) {
        log.info("消息发送成功:" + correlationData);
        System.out.println("ConfirmCallback:     " + "相关数据：" + correlationData);
        System.out.println("ConfirmCallback:     " + "确认情况：" + ack);
        System.out.println("ConfirmCallback:     " + "原因：" + cause);
    }

    /**
     * 消息失败要开启配置 支持消息发送失败返回队列,默认为false
     *     publisher-returns: true
     *  发送消息时路由错误会调用失败回调方法
     * */
    //消息发送失败回调方法
    @Override
    public void returnedMessage(Message message, int i, String s, String s1, String s2) {

        log.error("消息发送失败:" + new String(message.getBody()));

        System.out.println("ReturnCallback:     " + "消息：" + message);
        System.out.println("ReturnCallback:     " + "回应码：" + i);
        System.out.println("ReturnCallback:     " + "回应信息：" + s);
        System.out.println("ReturnCallback:     " + "交换机：" + s1);
        System.out.println("ReturnCallback:     " + "路由键：" + s2);

    }

    /**
     * 发送消息，不需要实现任何接口，供外部调用
     *
     * @param messageVo
     */
    public void send(MessageVo messageVo) {
        //每个来发送的消息都需要配备一个 CorrelationData 相关数据对象，CorrelationData 对象内部只有一个自 id 属性，用来表zd示当前消息唯一性。
        CorrelationData correlationData = new CorrelationData(UUID.randomUUID().toString());
        String exchange = RabbitConstant.EXCHANGE;
        String routingKey = messageVo.getRoutingKey();
        String message = JSON.toJSONString(messageVo);
        //使用convertAndSend方式发送消息，消息默认就是持久化的.
        rabbitTemplate.convertAndSend(exchange, routingKey, message, correlationData);
    }

    /**
     * direct交换机
     */
    public void sendDirect(List<DirectNews> list) {
        String s = JSON.toJSONString(list);
        MessageVo messageVo = new MessageVo();
        messageVo.setMsg(s);//存储的是string,所以读取的时候也要是String
        log.info("-----=----------------将要发送消息的内容---------- " + s);
        //每个来发送的消息都需要配备一个 CorrelationData 相关数据对象，CorrelationData 对象内部只有一个自 id 属性，用来表zd示当前消息唯一性。
        CorrelationData correlationData = new CorrelationData(UUID.randomUUID().toString());
        String exchange = DirectConstant.EXCHANGE;//设置交换机
        String routingKey = DirectConstant.QUEUE_PUSH;//设置路由
        //String routingKey = RabbitConstant.QUEUE_SMS_PUSH;
        String message = JSON.toJSONString(messageVo);
        //使用convertAndSend方式发送消息，消息默认就是持久化的.
        rabbitTemplate.convertAndSend(exchange, routingKey, message, correlationData);
    }

    /**
     * topic交换机
     */
    public void sendTopic(List<DirectNews> list) {
        String s = JSON.toJSONString(list);
        MessageVo messageVo = new MessageVo();
        messageVo.setMsg(s);//存储的是string,所以读取的时候也要是String
        log.info("-----=----------------TOPIC exchange将要发送消息的内容---------- " + s);
        //每个来发送的消息都需要配备一个 CorrelationData 相关数据对象，CorrelationData 对象内部只有一个自 id 属性，用来表zd示当前消息唯一性。
        CorrelationData correlationData = new CorrelationData(UUID.randomUUID().toString());
        String exchange = TopicConstant.TOPIC_EXCHANGE;//设置交换机
        String routingKey = TopicConstant.TOPIC_LEFT_QUEUE;//这里不能设置通配符路由吗，代码报错？
        String message = JSON.toJSONString(messageVo);
        //使用convertAndSend方式发送消息，消息默认就是持久化的.
        rabbitTemplate.convertAndSend(exchange, routingKey, message, correlationData);
    }

    /**
     * 扇形交换机,队列绑定交换机的时候不设置路由，发消息也不需要路由吗？
     */
    public void sendfanout(List<DirectNews> list) {
        String s = JSON.toJSONString(list);
        MessageVo messageVo = new MessageVo();
        messageVo.setMsg(s);//存储的是string,所以读取的时候也要是String
        log.info("-----=----------------fanout exchange将要发送消息的内容---------- " + s);
        //每个来发送的消息都需要配备一个 CorrelationData 相关数据对象，CorrelationData 对象内部只有一个自 id 属性，用来表zd示当前消息唯一性。
        CorrelationData correlationData = new CorrelationData(UUID.randomUUID().toString());
        String exchange = FanoutConstant.FANOUT_EXCHANGE;//设置fanout交换机

        String message = JSON.toJSONString(messageVo);
        //使用convertAndSend方式发送消息，消息默认就是持久化的.
        rabbitTemplate.convertAndSend(exchange, null, message, correlationData);
    }

    /**
     * 扇形交换机
     *
     * @param messageVo
     */
    public void send2(MessageVo messageVo) {
        //每个来发送的消息都需要配备一个 CorrelationData 相关数据对象，CorrelationData 对象内部只有一个自 id 属性，用来表zd示当前消息唯一性。
        CorrelationData correlationData = new CorrelationData(UUID.randomUUID().toString());
        String exchange = RabbitConstant.FANOUT_EXCHANGE;
        String queue = messageVo.getQueue();
        String message = JSON.toJSONString(messageVo);

        rabbitTemplate.convertAndSend(exchange, queue, message, correlationData);
    }


}
